
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "../include/NAry.h"
#include "../include/BST.h"
#include "../include/BlockOperations.h"

extern NAryNode *nAryRoot;
extern int META_HEADER_START, FILE_DESCRIPTORS_START, FREE_LIST_START, BLOCKS_START;
extern fileSystemInfo fileSysInfo;

/*
NAryNode* getNAryNode(char *path, char *fileType)
{
	char *parent = NULL, *file = NULL, *directory=NULL;
	char delims[] = "/";
	printf("\nGot path:%s...", path);
	parent = strtok( path, delims );
	printf("\nParent is: %s...", parent);
	file = strtok( NULL, delims );
	printf("\nFile is: %s", file);
	if(parent==NULL)
		return nAryRoot;
	NAryNode* pathNode = nAryRoot, *node, *temp;
	while( file != NULL ) 
	{	 
		//Search for parent and add It if Not Present...
		node = pathNode->directories;
		while( node!=NULL && strcmp( (node->fd.fileName), parent)!=0 )node=node->next;
		if(node == NULL)
			return NULL;		//Path Doesnot exist -- returns NULL
		else
			pathNode=node;
		parent = file;
	    	file = strtok( NULL, delims );
	}
	file = parent;
	printf("Path Node is: %s, File is: %s", pathNode->fd.fileName, file);
	printf("File Type is: %s", fileType);
	if( strcmp(fileType, "dir")==0 )
	{
		//Check if any directory Doesnot exist     -- returns NULL
		printf("Deleting dir");
		node = pathNode->directories;
		while(node!=NULL)
		{
			printf("Comparing %s with %s", node->fd.fileName, file);
			if( strcmp(node->fd.fileName, file)==0 )
				return node;	
			node = node->next;
		}
	}
	else
	{
		//Check if any File Doesnot exist     -- returns NULL
		node = pathNode->files;
		while(node!=NULL)
		{
			if( strcmp(node->fd.fileName, file)==0 )
				return node;	
			node = node->next;
		}
	}
	return NULL;		//No File or Directory Found...
}

fileDescriptor* getNAryNodeFileDescriptor(char *path, char *fileType)
{
	NAryNode *temp = getNAryNode(path, fileType);
	return (&temp->fd);
}
*/
NAryNode* insertNAryNode(fileDescriptor *fd)
{
	if(fd == NULL)	//Creating Root...
	{
		nAryRoot = (NAryNode*)malloc(sizeof(NAryNode));
		nAryRoot->next = NULL;
		nAryRoot->prev = NULL;		
		nAryRoot->parent = NULL;
		nAryRoot->files = NULL;
		nAryRoot->directories = NULL;
		strcpy(nAryRoot->fd.fileName, "");
		strcpy(nAryRoot->fd.fileType, "dir");
		strcpy(nAryRoot->fd.locationFullPath, "");
		return nAryRoot;
	}
	char ch[100];
	strcpy(ch, fd->locationFullPath);
	int totalLength = strlen(fd->locationFullPath);
	int fileNameLength = strlen(fd->fileName);
	int parentNameLength = totalLength - fileNameLength;
	if(parentNameLength==1)	//	Root ( / )
		ch[parentNameLength]='\0';
	else
		ch[parentNameLength-1] = '\0';

	BSTNode *bstNode = search_bst_node(ch);
	NAryNode *parent = bstNode->nAryNode;
		
	//NAryNode *parent = getNAryNode(ch, fd->fileType);

	NAryNode *temp = (NAryNode*)malloc(sizeof(NAryNode));
	temp->fd = (*fd);
	temp->fd.fileSize = (*fd).fileSize;
	strcpy(temp->fd.fileName, fd->fileName);
	strcpy(temp->fd.locationFullPath, fd->locationFullPath);
	temp->files = NULL;
	temp->directories = NULL;
	temp->parent = parent;
	temp->prev = NULL;
	temp->next = NULL;

	if(strcmp(fd->fileType, "dir")==0)
	{
		temp->next = parent->directories;
		if(temp->next!=NULL)
			temp->next->prev = temp;
		parent->directories = temp;
	}
	else
	{
		temp->next = parent->files;
		if(temp->next!=NULL)
			temp->next->prev = temp;
		parent->files = temp;
	}
	return temp;
}
/*
NAryNode* insertNAryNode(fileDescriptor *fd)
{
	if(fd == NULL)	//Creating Root...
	{
		nAryRoot = (NAryNode*)malloc(sizeof(NAryNode));
		nAryRoot->next = NULL;
		nAryRoot->prev = NULL;		
		nAryRoot->parent = NULL;
		nAryRoot->files = NULL;
		nAryRoot->directories = NULL;
		strcpy(nAryRoot->fd.fileName, "");
		return nAryRoot;
	}
	printf("\n Before: %s, %s", fd->fileName, fd->locationFullPath);
	char ch[100];
	char delims[] = "/";
	char *parent = NULL, *file = NULL, *directory=NULL;
	strcpy(ch, fd->locationFullPath);
	parent = strtok( ch, delims );
	file = strtok( NULL, delims );
	printf("\nActual Parent: %s, Actual File: %s\n", parent, file);
	NAryNode* pathNode = nAryRoot, *node = nAryRoot->directories, *temp;
	while( file != NULL ) 
	{	 
		//Search for parent and add It if Not Present...
		node = pathNode->directories;
		while( node!=NULL && strcmp( (node->fd.fileName), parent)!=0 )
		{
			printf("\r\nparent: %s, file: %s", parent, node->fd.fileName);
			node=node->next;
		}
		if(node == NULL)
		{
			node = pathNode->directories;
			//Creating a new parent --For mkdir -p option--
			printf("Found Unknown Directory %s", parent);
			fileDescriptor f;
			strcpy(f.locationFullPath, pathNode->fd.locationFullPath);
			strcat(f.locationFullPath, "/");
			strcat(f.locationFullPath, parent);
			strcpy(f.fileName, parent);
			f.block = -1;
			strcpy(f.fileType, "dir");

			temp = (NAryNode*)malloc(sizeof(NAryNode));
			temp->fd = f;
			temp->files = NULL;
			temp->directories = NULL;
			temp->next = pathNode->directories;
			if(temp->next!=NULL)
				temp->next->prev = temp;
			temp->prev = NULL;
			temp->parent = pathNode;
			pathNode->directories = temp;
			
			fileSysInfo.usedFileDescriptors++;
			
			pathNode = temp;	
		}
		else
			pathNode=node;
		parent = file;
	    	file = strtok( NULL, delims );

	}

	file = parent;
	temp = (NAryNode*)malloc(sizeof(NAryNode));
	temp->fd = (*fd);
	temp->files = NULL;
	temp->directories = NULL;
	
	if( strcmp(fd->fileType, "dir")==0 )
	{
		//Check if any directory Already exists     -- returns -1
		node = pathNode->directories;
		printf("\n%s-- file", file);
		if(pathNode == nAryRoot)
			printf("Root is the path Node");
		while(node!=NULL)
		{
			if( strcmp(node->fd.fileName, file)==0 )
				return NULL;	
			node = node->next;
		}
		temp->next = pathNode->directories;
		if(temp->next != NULL)
			temp->next->prev = temp;
		temp->prev = NULL;
		temp->parent = pathNode;
		pathNode->directories = temp;
		printf("\n%s--fileNametemp", temp->fd.fileName);
		printf("\n%s--fileNametemp", pathNode->directories->fd.fileName);
	}
	else
	{
		//Check if any File Already exists     -- returns -2
		node = pathNode->files;
		while(node!=NULL)
		{
			if( strcmp(node->fd.fileName, file)==0 )
				return NULL;
			node = node->next;	
		}
		temp->next = pathNode->files;
		if(temp->next != NULL)
			temp->next->prev = temp;
		temp->prev = NULL;
		temp->parent = pathNode;
		pathNode->files = temp;
	}
	return temp;
}
*/

int freeDirectory(NAryNode *temp)
{
	if(temp!=NULL)
	{
		if(strcmp(temp->fd.fileType, "dir")!=0) {	//Incase of file Free the block too...
			fileSysInfo.freeBlock--;
			int blockno = temp->fd.block;
			int freeListBlockPosition = FREE_LIST_START + sizeof(int)*(fileSysInfo.freeBlock);
			FILE *fp = fopen(fileSysInfo.fileSystemLabel, "rb+");
			fseek(fp, freeListBlockPosition, SEEK_SET);
			fwrite(&(blockno), sizeof(int), 1, fp);
			fclose(fp);	
		}
		fileSysInfo.usedFileDescriptors--;
		freeDirectory(temp->next);
		freeDirectory(temp->files);
		freeDirectory(temp->directories);
		free(temp);
	}
	return 1;		
}

int deleteNAryNode(char *path, char *fileType) 		//Returns the block No. if Existing else returns -2
{
	//printf("Got to delete:%s...", path);
	//printf("File type: %s", fileType); 
	BSTNode *bstNode = search_bst_node(path);
	NAryNode *temp = bstNode->nAryNode;
	//printf("\nFile: %s", bstNode->nAryNode->fd.locationFullPath);
	//printf("\nParent: %s", bstNode->nAryNode->parent->fd.locationFullPath);

	if( strcmp(fileType, "dir")==0 )
	{
		if(temp->parent->directories == temp)
			temp->parent->directories = temp->next;
		if(temp->next!=NULL)
			temp->next->prev = temp->prev;
		if(temp->prev!=NULL)
			temp->prev->next = temp->next;	
		int block = temp->fd.block;
		freeDirectory(temp->directories);
		freeDirectory(temp->files);
		free(temp);
		return block;
	}
	else
	{
		if(temp->parent->files == temp)
			temp->parent->files = temp->next;
		if(temp->next!=NULL)
			temp->next->prev = temp->prev;
		if(temp->prev!=NULL)
			temp->prev->next = temp->next;
		int block = temp->fd.block;
		free(temp);
		return block;
	}	
}

int changeFullPath(NAryNode *node)
{
	if(node!=NULL)
	{
		strcpy(node->fd.locationFullPath, node->parent->fd.locationFullPath);
		strcat(node->fd.locationFullPath, "/");
		strcat(node->fd.locationFullPath, node->fd.fileName);
		changeFullPath(node->next);
		changeFullPath(node->files);
		changeFullPath(node->directories);
	}
	return 1;
}

NAryNode* moveNAryNode(char *path1, char *path2, char *fileType)
{
	BSTNode *bstNode = search_bst_node(path1);
	NAryNode *temp1 = bstNode->nAryNode;
	
	bstNode = search_bst_node(path2);
	NAryNode *temp2 = bstNode->nAryNode;

	//NAryNode *temp1 = getNAryNode(path1, fileType);
	//NAryNode *temp2 = getNAryNode(path2, "dir");
	//printf("Came into Move Files");
	if( strcmp(fileType, "dir")==0 )
	{
		/*
		printf("Parent: %s", temp1->parent->fd.fileName);
		printf("Dest: %s", temp2->fd.fileName);
		if(temp2->directories!=NULL)
			printf("Dest Dir: %s", temp2->directories->fd.fileName);
		*/
		if(temp1->parent->directories == temp1)
			temp1->parent->directories = temp1->next;
		if(temp1->next!=NULL)
			temp1->next->prev = temp1->prev;
		if(temp1->prev!=NULL)
			temp1->prev->next = temp1->next;	
		temp1->next = temp2->directories;
		if(temp1->next != NULL)
			temp1->next->prev = temp1;
		temp1->prev = NULL;
		temp1->parent = temp2;
		temp2->directories = temp1;
		strcpy(temp1->fd.locationFullPath, temp2->fd.locationFullPath);
		strcat(temp1->fd.locationFullPath, "/");
		strcat(temp1->fd.locationFullPath, temp1->fd.fileName);
		changeFullPath(temp1->files);
		changeFullPath(temp1->directories);
	}	
	else
	{
		if(temp1->parent->files == temp1)
			temp1->parent->files = temp1->next;
		if(temp1->next!=NULL)
			temp1->next->prev = temp1->prev;
		if(temp1->prev!=NULL)
			temp1->prev->next = temp1->next;
		temp1->next = temp2->files;
		if(temp1->next != NULL)
			temp1->next->prev = temp1;
		temp1->prev = NULL;
		temp1->parent = temp2;
		temp2->files = temp1;
		strcpy(temp1->fd.locationFullPath, temp2->fd.locationFullPath);
		strcat(temp1->fd.locationFullPath, "/");
		strcat(temp1->fd.locationFullPath, temp1->fd.fileName);
	}
	return temp1;
}

int copyFullDirectory(NAryNode **node1, NAryNode *node2, NAryNode *parent, NAryNode *prev)
{
	if(node2 != NULL)
	{
		*node1 = (NAryNode*)malloc(sizeof(NAryNode));
		(*node1)->parent = parent;
		(*node1)->prev = prev;
		strcpy((*node1)->fd.fileName, node2->fd.fileName);
		strcat((*node1)->fd.locationFullPath, node2->fd.locationFullPath);
		strcat((*node1)->fd.fileType, node2->fd.fileType);
		(*node1)->fd.block = node2->fd.block;
		copyFullDirectory(&((*node1)->next), node2->next, parent, *node1);
		copyFullDirectory(&((*node1)->files), node2->files, *node1, NULL);
		copyFullDirectory(&((*node1)->directories), node2->directories, *node1, NULL);
	}
	return 1;
}

NAryNode* copyNAryNode(char *path1, char *path2, char *fileType)
{
	BSTNode *bstNode = search_bst_node(path1);
	NAryNode *temp1 = bstNode->nAryNode;
	
	bstNode = search_bst_node(path2);
	NAryNode *temp2 = bstNode->nAryNode;

	//NAryNode *temp1 = getNAryNode(path1, fileType);
	//NAryNode *temp2 = getNAryNode(path2, "dir");
	if(temp1==NULL || temp2==NULL)
	{
		//printf("Paths doesnot exist");
		return NULL;
	}
	NAryNode *temp3 = (NAryNode*)malloc(sizeof(NAryNode));
	if( strcmp(fileType, "dir")==0 )
	{
		*temp3 = *temp1;
		temp3->next = temp2->files;
		temp3->prev = NULL;
		temp3->parent = temp2;
		temp2->files = temp3;
		strcpy(temp3->fd.locationFullPath, temp2->fd.locationFullPath);
		strcat(temp3->fd.locationFullPath, "/");
		strcat(temp3->fd.locationFullPath, temp3->fd.fileName);
		copyFullDirectory(&(temp3->files), temp1->files, temp3, NULL);
		copyFullDirectory(&(temp3->directories), temp1->directories, temp3, NULL);
		changeFullPath(temp3->files);
		changeFullPath(temp3->directories);
		fileSysInfo.usedFileDescriptors++;
	}	
	else
	{
		
		*temp3 = *temp1;
		temp3->next = temp2->files;
		temp3->prev = NULL;
		temp3->parent = temp2;
		temp2->files = temp3;
		strcpy(temp3->fd.locationFullPath, temp2->fd.locationFullPath);
		strcat(temp3->fd.locationFullPath, "/");
		strcat(temp3->fd.locationFullPath, temp3->fd.fileName);
		fileSysInfo.usedFileDescriptors++;
		
		/* Copying the Block */
		block b;
		int freeBlock = get_free_block();	
		FILE *fp = fopen(fileSysInfo.fileSystemLabel, "rb+");
		fseek(fp, BLOCKS_START + (temp1->fd.block)*sizeof(block), SEEK_SET);
		fread(&b, sizeof(block), 1, fp);
		int freeBlockPosition = BLOCKS_START + sizeof(block)*freeBlock;
		fseek(fp, freeBlockPosition, SEEK_SET);
		fwrite( &b, sizeof(block), 1,fp);
		fclose(fp);

		temp3->fd.block = freeBlock;
	}
	return temp3;
}

int displayNAryNode(char *path, int option, FILE *fp)
{
	BSTNode *bstNode = search_bst_node(path);
	NAryNode *temp = NULL;
	temp = bstNode->nAryNode;
	//NAryNode* temp = getNAryNode(path, "dir");
	NAryNode* temp2;
	temp2 = temp->files;
	//printf("\r\n--Directory : %s --", temp->fd.fileName);
	fprintf(fp, "\r\n--Directory : %s --", temp->fd.fileName);
	//printf("\r\nFILES:\r\n");
	fprintf(fp, "%s", "\r\nFILES:\r\n");
	while(temp2!=NULL)
	{
		//printf("%s, %s\t", temp2->fd.fileName, temp2->fd.locationFullPath);
		fprintf(fp, "%s, %s\t", temp2->fd.fileName, temp2->fd.locationFullPath);
		temp2 = temp2->next;

	}
	{
		NAryNode* temp2;
		temp2 = temp->directories;
		//printf("\r\nDIRECTORIES:\r\n");
		fprintf(fp, "\r\nDIRECTORIES:\r\n");
		while(temp2!=NULL)
		{
			//printf("%s, %s\t", temp2->fd.fileName, temp2->fd.locationFullPath);
			fprintf(fp, "%s, %s\t", temp2->fd.fileName, temp2->fd.locationFullPath);				
			temp2 = temp2->next;
		}
	}
	//printf("\r\n\r\n\r\n");
	if(option==1)
	{
		temp2 = temp->directories;
		while(temp2!=NULL)
		{
			displayNAryNode(temp2->fd.locationFullPath, 1, fp);
			temp2 = temp2->next;
		}
	}
	return 1;
}

int recurse_nary(FILE *fp, NAryNode* temp, int *count)
{
	if(temp!=NULL)
	{
		(*count)++;
		fwrite(&(temp->fd), sizeof(fileDescriptor), 1, fp);
		recurse_nary(fp, temp->files, count);
		recurse_nary(fp, temp->next, count);
		recurse_nary(fp, temp->directories, count);
	}
	return 1;
}

int persist_nary()
{
	FILE *fp = fopen(fileSysInfo.fileSystemLabel, "rb+");
	fseek(fp, FILE_DESCRIPTORS_START, SEEK_SET);
	int count=0;
	recurse_nary(fp, nAryRoot->files, &count);
	recurse_nary(fp, nAryRoot->directories, &count);
	fclose(fp);
	fileSysInfo.usedFileDescriptors = count;
	return 1;
}
